
package sg.edu.nus.iss.vmcs.customer;
/*
 * Copyright 2003 ISS.
 * The contents contained in this document may not be reproduced in any
 * form or by any means, without the written permission of ISS, other
 * than for the purpose for which it has been supplied.
 *
 */

import java.awt.Frame;

import sg.edu.nus.iss.vmcs.service.TransactionDisplayService;
import sg.edu.nus.iss.vmcs.store.DrinksBrand;
import sg.edu.nus.iss.vmcs.store.Store;
import sg.edu.nus.iss.vmcs.store.StoreController;
import sg.edu.nus.iss.vmcs.store.StoreItem;
import sg.edu.nus.iss.vmcs.system.MainController;
import sg.edu.nus.iss.vmcs.system.SimulatorControlPanel;

/**
 * This control object coordinates the customer transactions for selection of a drink brand,
 * coin input, storage of coins and termination requests for ongoing transactions.
 * @author Team SE16T5E
 * @version 1.0 2008-10-01
 */
public class TransactionController {
	private MainController mainCtrl;
	private DispenseController dispenseCtrl;
	private ChangeGiver changeGiver;
	private CoinReceiver coinReceiver;
	private TransactionDisplayService transactionService;
	/**Set to TRUE when change is successfully issued during the transaction.*/
	private boolean changeGiven=false;
	/**Set to TRUE when the drink is successfully dispensed during the transaction.*/
	private boolean drinkDispensed=false;
	/**Price of the selected drink.*/
	private int price=0;
	/**Identifier of the selected drink.*/
	private int selection=-1;
	private TransactionState trxState;	
	private TransactionState_CoinsObtained trxState_CoinsObtained;
	private TransactionState_DrinkSelected trxState_DrinkSelected;
	private TransactionState_TransactionCompleted trxState_TransactionCompleted;
	private TransactionState_TerminateFaultyAction trxState_TerminateFaultyAction;
	private TransactionState_TransationTerminated trxState_TransactionTerminated;
	private TransactionState_TransactionCancelled trxState_TransactionCancelled;
	private TransactionState_CloseDown trxState_CloseDown;
	private TransactionState_CustomerPanelRefreshed trxState_CustomerPanelRefreshed;
	
	/**
	 * This constructor creates an instance of the TransactionController.
	 * @param mainCtrl the MainController.
	 */
	public TransactionController(MainController mainCtrl) {
		this.mainCtrl = mainCtrl;
		dispenseCtrl=new DispenseController(this);
		coinReceiver=new CoinReceiver(this);
		changeGiver=new ChangeGiver(this);
		transactionService = TransactionDisplayService.getInstance();
		setState(getTransactionState_TransactionCompleted());
	}
		
	/**
	 * This method returns TransactionState_CoinsObtained
	 * */
	private TransactionState getTransactionState_CoinsObtained()
	{
		if(trxState_CoinsObtained == null)
			trxState_CoinsObtained = new TransactionState_CoinsObtained();
		
		return trxState_CoinsObtained;
	}
	
	/**
	 * This method returns TransactionState_DrinkSelected 
	 */
	private TransactionState getTransactionState_DrinkSelected()
	{
		if(trxState_DrinkSelected == null)
			trxState_DrinkSelected = new TransactionState_DrinkSelected();
		
		return trxState_DrinkSelected;
	}
	
	/**
	 * This method returns TransactionState_TransactionCompleted 
	 */
	private TransactionState getTransactionState_TransactionCompleted()
	{
		if(trxState_TransactionCompleted == null)
			trxState_TransactionCompleted = new TransactionState_TransactionCompleted();
		
		return trxState_TransactionCompleted;
	}
	
	/**
	 * This method returns TransactionState_TerminateFaultyAction 
	 */
	private TransactionState getTransactionState_TerminateFaultyAction()
	{
		if(trxState_TerminateFaultyAction == null)
			trxState_TerminateFaultyAction = new TransactionState_TerminateFaultyAction();
		
		return trxState_TerminateFaultyAction;
	}
	
	/**
	 * This method returns getTransactionState_TransactionTerminated 
	 */
	private TransactionState getTransactionState_TransactionTerminated()
	{
		if(trxState_TransactionTerminated == null)
			trxState_TransactionTerminated = new TransactionState_TransationTerminated();
		
		return trxState_TransactionTerminated;
	}
	
	/**
	 * This method returns getTransactionState_TransactionCancelled 
	 */
	private TransactionState getTransactionState_TransactionCancelled()
	{
		if(trxState_TransactionCancelled == null)
			trxState_TransactionCancelled = new TransactionState_TransactionCancelled();
		
		return trxState_TransactionCancelled;
	}
	
	/**
	 * This method returns getTransactionState_CloseDown 
	 */
	private TransactionState getTransactionState_CloseDown()
	{
		if(trxState_CloseDown == null)
			trxState_CloseDown = new TransactionState_CloseDown();
		
		return trxState_CloseDown;
	}
	
	/**
	 * This method returns getTransactionState_CustomerPanelRefreshed 
	 */
	private TransactionState getTransactionState_CustomerPanelRefreshed()
	{
		if(trxState_CustomerPanelRefreshed == null)
			trxState_CustomerPanelRefreshed = new TransactionState_CustomerPanelRefreshed();
		
		return trxState_CustomerPanelRefreshed;
	}
	
	/**
	 * Inner Class TransactionState, Base Class of all transaction state objects.
	 * Defines the standard implementation of few methods which are common across the sub classes of transaction state 
	 */
	public class TransactionState {
		
		public void displayCustomerPanel(TransactionController trxController) {
			SimulatorControlPanel scp = mainCtrl.getSimulatorControlPanel();
			transactionService.setCustPanel(new CustomerPanel((Frame) scp, trxController));
		    
			transactionService.displayCustomerPanel();
			dispenseCtrl.updateDrinkPanel();
			dispenseCtrl.allowSelection(true);
			changeGiver.displayChangeStatus();
			coinReceiver.setActive(false);
			changeState(trxController, trxController.getTransactionState_CustomerPanelRefreshed());
		}
		
		public void startTransaction(TransactionController trxCtrlr, int drinkIdentifier) {}
		public void processMoneyReceived(TransactionController trxCtrlr, int total) {}
		public void completeTransaction(TransactionController trxCtrlr) {}
		
		/**
		 * If the TransactionController is informed that a fault was discovered while
		 * dispensing a drink, giving change or storing Coins, it will use this method
		 * to deactivate the Drink Selection Box and instruct the CoinReceiver to refund the
		 * money inserted by the customer.
		 */
		public void terminateFault(TransactionController trxCtrlr){
			System.out.println("TerminateFault: Begin");
			dispenseCtrl.allowSelection(false);
			coinReceiver.refundCash();
			refreshMachineryDisplay();
			System.out.println("TerminateFault: End");
			changeState(trxCtrlr,trxCtrlr.getTransactionState_TerminateFaultyAction());
		}
		
		/*
		* If the TransactionController receivers a request to terminate the current
		 * transaction then following will occur:
		 * <br>
		 * 1- If there is no transaction in progress or coin input is completed then the 
		 * CustomerPanel will be instructed to deactivate the DrinkSelectionBox&#46;
		 * <br>
		 * 2- If coin input is not yet complete, the CoinReceiver will be instructed to stop
		 * coin input and refund the money entered so far&#46;
		 * <br>
		 * 3- The DrinkSelectionBox is then reset to allow further transactions&#46;
		 */
		public void terminateTransaction(TransactionController trxCtrlr){
				System.out.println("TerminateTransaction: Begin");
				dispenseCtrl.allowSelection(false);
				coinReceiver.stopReceive();
				coinReceiver.refundCash();
				transactionService.terminateTransactionServiceForCustomerPanel();
				refreshMachineryDisplay();
				System.out.println("TerminateTransaction: End");
				changeState(trxCtrlr,trxCtrlr.getTransactionState_TransactionTerminated());
			}
	
		/**
		 * This method will cancel an ongoing customer transaction.
		 */
		public void cancelTransaction(TransactionController trxCtrlr){
				System.out.println("CancelTransaction: Begin");
				coinReceiver.stopReceive();
				coinReceiver.refundCash();
				dispenseCtrl.allowSelection(true);
				refreshMachineryDisplay();
				System.out.println("CancelTransaction: End");
				changeState(trxCtrlr,trxCtrlr.getTransactionState_TransactionCancelled());
			}
		
		/**
		 * This method will close down the transaction control function of the vending
		 * machine.
		 */
		public void closeDown(TransactionController trxCtrlr) {
			transactionService.closeDownCustPanel();
			changeState(trxCtrlr, trxCtrlr.getTransactionState_CloseDown());
		}
		
		public void refreshCustomerPanel(TransactionController trxCtrlr) {
			//if(custPanel==null){
				//mainCtrl.getSimulatorControlPanel().setActive(SimulatorControlPanel.ACT_CUSTOMER,true);
			//}
		
			//dispenseCtrl.updateDrinkPanel();
			dispenseCtrl.allowSelection(true);
			changeGiver.displayChangeStatus();
			transactionService.setCustomerPanelTerminateButton(true);
			changeState(trxCtrlr, trxCtrlr.getTransactionState_CustomerPanelRefreshed());
		}
	
		public void changeState(TransactionController trxCtrlr, TransactionState transState) {
			trxCtrlr.setState(transState);
		}
	}
	
	/**
	 * Inner Class TransactionState, Base Class of all transaction in active state objects.
	 * Defines the standard implementation of few methods which are common across the sub classes of transaction in active state 
	 */
	public class TransactionState_InActive extends TransactionState{
		public void startTransaction(TransactionController trxCtrlr,int drinkIdentifier) {
			setSelection(drinkIdentifier);
			StoreItem storeItem=StoreController.getInstance().getStoreItem(Store.DRINK,drinkIdentifier);
			DrinksBrand drinksBrand=(DrinksBrand)storeItem.getContent();
			setPrice(drinksBrand.getPrice());
			changeGiver.resetChange();
			dispenseCtrl.ResetCan();
			changeGiver.displayChangeStatus();
			dispenseCtrl.allowSelection(false);
			coinReceiver.startReceiver();
			transactionService.setCustomerPanelTerminateButton(true);
			changeState(trxCtrlr, trxCtrlr.getTransactionState_DrinkSelected());
		}		
	}
	
	public class TransactionState_TransactionCompleted extends TransactionState_InActive {
	}
	
	public class TransactionState_DrinkSelected extends TransactionState {
		/**
		 * This method processes the money received by the Coin Receiver during the progress
		 * of a transaction&#46;  The following actions are performed during this method:
		 * <br>
		 * 1- The current total money inserted is obtained from the Coin Receiver&#46;
		 * <br>
		 * 2- If the received money is more than or equal to the price of the drink, 
		 * method CompleteTransaction of the Transaction Controller is triggered&#46;
		 * <br>
		 * 3- If the received money is less than the price of the drink, the Coin Receiver
		 * is instructed to continue receiving the coin&#46;
		 * @param total the total money received&#46;
		 */
		public void processMoneyReceived(TransactionController trxCtrlr, int total){
			if(total>=price)
			{
				changeState(trxCtrlr, trxCtrlr.getTransactionState_CoinsObtained());
				trxCtrlr.getTransactionState_CoinsObtained().completeTransaction(trxCtrlr);
			}
			else{
				coinReceiver.continueReceive();
			}
		}
	}

	public class TransactionState_CoinsObtained extends TransactionState {
		/**
		 * This method is performed when the Transaction Controller is informed that coin
		 * entry is complete and the money received is sufficient to dispense the drink.
		 * The following actions are performed.
		 * <br>
		 * 1- Dispense the drink.
		 * <br>
		 * 2- Give change if necessary.
		 * <br>
		 * 3- Store the Coins that have been entered into the Cash Store.
		 * <br>
		 * 4- Reset the Drink Selection Box to allow further transactions.
		 */
		public void completeTransaction(TransactionController trxCtrlr){
			System.out.println("CompleteTransaction: Begin");
			dispenseCtrl.dispenseDrink(selection);
			int totalMoneyInserted=coinReceiver.getTotalInserted();
			int change=totalMoneyInserted-price;
			if(change>0){
				changeGiver.giveChange(change);
			}
			else{
				transactionService.setChange(0);
			}
			coinReceiver.storeCash();
			dispenseCtrl.allowSelection(true);
			
			refreshMachineryDisplay();
			System.out.println("CompleteTransaction: End"); 
			changeState(trxCtrlr, trxCtrlr.getTransactionState_TransactionCompleted());
		}
	}
		
	public class TransactionState_TerminateFaultyAction extends TransactionState_InActive {
		
	}
	
	public class TransactionState_TransationTerminated extends TransactionState_InActive {
	}
	
	public class TransactionState_TransactionCancelled extends TransactionState_InActive {
	}
	
	public class TransactionState_CloseDown extends TransactionState_InActive {
	}
	
	public class TransactionState_CustomerPanelRefreshed extends TransactionState_InActive {
	}
	
	private void setState(TransactionState trxState){
		this.trxState = trxState;
	}
	
	/**
	 * This method returns the MainController.
	 * @return the MainController.
	 */
	public MainController getMainController() {
		return mainCtrl;
	}

	/*
	 * This method return transactionDisplayService
	 */
	public TransactionDisplayService getTransactionDisplayService()
	{
		return this.transactionService;
	}
	
	/**
	 * This method displays and initialize the CustomerPanel.
	 */
	public void displayCustomerPanel() {
		/*SimulatorControlPanel scp = mainCtrl.getSimulatorControlPanel();
		transactionService.setCustPanel(new CustomerPanel((Frame) scp, this));
	    
		transactionService.displayCustomerPanel();
		dispenseCtrl.updateDrinkPanel();
		dispenseCtrl.allowSelection(true);
		changeGiver.displayChangeStatus();
		coinReceiver.setActive(false);*/
		this.trxState.displayCustomerPanel(this);
	}
	
	/**
	 * This method will start the customer transaction&#46; It receives the identification
	 * for the selected drink brand (item) from the Customer Panel&#46; The following
	 * actions are performed in the method:
	 * <br>
	 * 1- The price of the selected item is obtained&#46;
	 * <br>
	 * 2- The Refund/ Change Tray Display is reset&#46;
	 * <br>
	 * 3- The Can Collection Box is reset&#46;
	 * <br>
	 * 4- The Drink Selection Box is deactivated to disallow the selection of further
	 * drinks when the transaction is in progress&#46;
	 * <br>
	 * 5- The Coin Receiver will be instructed to start receiving the coins&#46;
	 * @param drinkIdentifier the drink brand item identifier.
	 */
	public void startTransaction(int drinkIdentifier){
		this.trxState.startTransaction(this, drinkIdentifier);
	}
	
	/**
	 * This method processes the money received by the Coin Receiver during the progress
	 * of a transaction&#46;  The following actions are performed during this method:
	 * <br>
	 * 1- The current total money inserted is obtained from the Coin Receiver&#46;
	 * <br>
	 * 2- If the received money is more than or equal to the price of the drink, 
	 * method CompleteTransaction of the Transaction Controller is triggered&#46;
	 * <br>
	 * 3- If the received money is less than the price of the drink, the Coin Receiver
	 * is instructed to continue receiving the coin&#46;
	 * @param total the total money received&#46;
	 */
	public void processMoneyReceived(int total){
		this.trxState.processMoneyReceived(this, total);
	}
	
	/**
	 * This method is performed when the Transaction Controller is informed that coin
	 * entry is complete and the money received is sufficient to dispense the drink.
	 * The following actions are performed.
	 * <br>
	 * 1- Dispense the drink.
	 * <br>
	 * 2- Give change if necessary.
	 * <br>
	 * 3- Store the Coins that have been entered into the Cash Store.
	 * <br>
	 * 4- Reset the Drink Selection Box to allow further transactions.
	 */
	public void completeTransaction(){
		this.trxState.completeTransaction(this);
	}
	
	/**
	 * If the TransactionController is informed that a fault was discovered while
	 * dispensing a drink, giving change or storing Coins, it will use this method
	 * to deactivate the Drink Selection Box and instruct the CoinReceiver to refund the
	 * money inserted by the customer.
	 */
	public void terminateFault(){
		trxState.terminateFault(this);
	}
	
	/**
	 * If the TransactionController receivers a request to terminate the current
	 * transaction then following will occur:
	 * <br>
	 * 1- If there is no transaction in progress or coin input is completed then the 
	 * CustomerPanel will be instructed to deactivate the DrinkSelectionBox&#46;
	 * <br>
	 * 2- If coin input is not yet complete, the CoinReceiver will be instructed to stop
	 * coin input and refund the money entered so far&#46;
	 * <br>
	 * 3- The DrinkSelectionBox is then reset to allow further transactions&#46;
	 */
	public void terminateTransaction(){
		trxState.terminateTransaction(this);
	}
	
	/**
	 * This method will cancel an ongoing customer transaction.
	 */
	public void cancelTransaction(){
		trxState.cancelTransaction(this);
	}
	
	/**
	 * This method refreshes the CustomerPanel when maintainer logs-out.
	 */
	public void refreshCustomerPanel(){
		/*
		//if(custPanel==null){
			//mainCtrl.getSimulatorControlPanel().setActive(SimulatorControlPanel.ACT_CUSTOMER,true);
		//}
		
		//dispenseCtrl.updateDrinkPanel();
		dispenseCtrl.allowSelection(true);
		changeGiver.displayChangeStatus();
		transactionService.setCustomerPanelTerminateButton(true); */
		this.trxState.refreshCustomerPanel(this);
	}
	
	/**
	 * This method will close down the transaction control function of the vending
	 * machine.
	 */
	public void closeDown() {
		//transactionService.closeDownCustPanel();
		trxState.closeDown(this);
	}

	/**
	 * This method sets whether the change is given.
	 * @param changeGiven TRUE the change is given, otherwise FALSE.
	 */
	public void setChangeGiven(boolean changeGiven) {
		this.changeGiven = changeGiven;
	}

	/**
	 * This method returns whether the change is given.
	 * @return TRUE if the change is given, otherwise FALSE.
	 */
	public boolean isChangeGiven() {
		return changeGiven;
	}

	/**
	 * This method sets whether the drink is dispensed.
	 * @param drinkDispensed TRUE the drink is dispensed, otherwise, FALSE.
	 */
	public void setDrinkDispensed(boolean drinkDispensed) {
		this.drinkDispensed = drinkDispensed;
	}

	/**
	 * This method returns whether the drink is dispensed.
	 * @return TRUE if the drink is dispensed, otherwise FALSE.
	 */
	public boolean isDrinkDispensed() {
		return drinkDispensed;
	}

	/**
	 * This method sets the price of the selected drink.
	 * @param price the price of the selected drink.
	 */
	public void setPrice(int price) {
		this.price = price;
	}

	/**
	 * This method returns the price of the selected drink.
	 * @return the price of the selected drink.
	 */
	public int getPrice() {
		return price;
	}

	/**
	 * This method sets the selected drink index.
	 * @param selection the selected drink index.
	 */
	public void setSelection(int selection) {
		this.selection = selection;
	}

	/**
	 * This method returns the selected drink index.
	 * @return the selected drink index.
	 */
	public int getSelection() {
		return selection;
	}
	
	/**
	 * This method returns the DispenseController.
	 * @return the DispenseController.
	 */
	public DispenseController getDispenseController(){
		return dispenseCtrl;
	}
	
	/**
	 * This method returns the ChangeGiver.
	 * @return the ChangeGiver.
	 */
	public ChangeGiver getChangeGiver(){
		return changeGiver;
	}
	
	/**
	 * This method returns the CoinReceiver.
	 * @return the CoinReceiver.
	 */
	public CoinReceiver getCoinReceiver(){
		return coinReceiver;
	}
	
	/**
	 * This method refreshes the MachinerySimulatorPanel.
	 */
	public void refreshMachineryDisplay(){
		mainCtrl.refreshMachineryDisplay(); 
		
	}
	/**
	 * This method refreshes the MachinerySimulatorPanel.
	 */
	public void updateDrinkPanel(){
		dispenseCtrl.updateDrinkPanel();	
	}


	
	/**
	 * This method refreshes the CustomerPanel when maintainer logs-out.
	 */
	public void updateDisplay(Object arg1){
		/*
		if(custPanel==null){
			mainCtrl.getSimulatorControlPanel().setActive(SimulatorControlPanel.ACT_CUSTOMER,true);
		}
		*/
		//dispenseCtrl.updateDrinkPanel();
		dispenseCtrl.allowSelection(true);
		changeGiver.displayChangeStatus();
		transactionService.setCustomerPanelTerminateButton(true);
	}
	/**
	 * This method will nullify reference to customer panel.
	 */
	public void nullifyCustomerPanel(){
		transactionService.setCustPanel(null);
	}
}//End of class TransactionController